package com.cui.project.api;


import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

/**
 * Python时间序列预测服务HTTP客户端
 * 
 * 功能说明：
 * 1. 封装与Python Flask预测服务的HTTP通信
 * 2. 支持单接口预测和批量预测两种模式
 * 3. 使用Apache HttpClient实现HTTP请求
 * 4. 配置了连接超时和响应超时，确保服务可用性
 * 5. 支持多种预测模型：Prophet、LSTM、ARIMA、集成模型
 * 
 * 架构设计：
 * - Java后端负责数据准备和结果存储
 * - Python服务负责复杂的时间序列预测算法
 * - 通过REST API实现跨语言服务调用
 * 
 * @author cui
 * @date 2025-11-02
 */
@Component
@Slf4j
public class PythonForecastClient {

    /**
     * Python预测服务的URL地址
     * 默认值：http://localhost:5000
     * 可通过配置文件覆盖：python.forecast.service.url
     */
    @Value("${python.forecast.service.url:http://localhost:5000}")
    private String pythonServiceUrl;

    /**
     * HTTP请求超时时间（毫秒）
     * 默认值：30000ms（30秒）
     * 可通过配置文件覆盖：python.forecast.service.timeout
     */
    @Value("${python.forecast.service.timeout:30000}")
    private int timeout;

    /**
     * JSON序列化/反序列化工具
     * 用于请求体和响应体的JSON转换
     */
    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * HTTP客户端实例
     * 使用Apache HttpClient的默认配置
     */
    private final CloseableHttpClient httpClient= HttpClients.createDefault();
    
    /**
     * HTTP请求配置
     * 设置三种超时参数确保请求不会无限期等待：
     * 1. ConnectTimeout：建立TCP连接的最大等待时间
     * 2. SocketTimeout：连接建立后，等待数据传输的超时时间
     * 3. ConnectionRequestTimeout：从连接池获取连接的最大等待时间
     */
    private final RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(5000)     // 连接超时：5秒（建立TCP连接的最大等待时间）
            .setSocketTimeout(timeout)     // 数据传输超时：10秒（连接建立后，等待数据的超时）
            .setConnectionRequestTimeout(2000) // 连接池超时：2秒（从连接池获取连接的最大等待时间）
            .build();
    /**
     * 调用Python服务进行单接口流量预测
     * 
     * 工作流程：
     * 1. 构造POST请求，目标端点为 /api/forecast
     * 2. 将请求参数序列化为JSON格式
     * 3. 发送HTTP请求到Python服务
     * 4. 解析响应并返回预测结果
     * 
     * 预测内容：
     * - 未来指定小时数的流量预测值
     * - 每个预测点的置信区间（上界、下界）
     * - 模型准确度指标
     *
     * @param request 预测请求对象，包含：
     *                - interfaceId: 接口ID
     *                - historicalData: 历史流量数据（时间戳+请求数）
     *                - forecastHours: 预测未来多少小时（默认24小时）
     *                - modelType: 预测模型类型（prophet/lstm/arima/ensemble）
     *                - confidenceLevel: 置信水平（默认0.95，即95%置信区间）
     * @return 预测响应对象，包含：
     *         - predictions: 预测数据点列表（时间戳+预测值+置信区间）
     *         - accuracyMetrics: 准确度指标（如MAE、RMSE等）
     *         - modelType: 实际使用的模型类型
     * @throws RuntimeException 当Python服务调用失败或返回错误时抛出
     */
    public  ForecastResponse forecast(ForecastRequest request){
        // 构造Python服务的预测接口URL
        String url=pythonServiceUrl+"/api/forecast";
        try {
            // 创建HTTP POST请求
            HttpPost httpPost = new HttpPost(url);
            httpPost.setHeader("Content-Type", "application/json");
            httpPost.setConfig(requestConfig);

            // 将请求对象序列化为JSON字符串
            String json = objectMapper.writeValueAsString(request);
            httpPost.setEntity(new StringEntity(json, StandardCharsets.UTF_8));

            log.info("调用Python预测服务: interface_id={}, model_type={}",
                    request.getInterfaceId(), request.getModelType());

            // 执行HTTP请求并自动关闭资源
            try (CloseableHttpResponse response = httpClient.execute(httpPost);){
                // 读取响应体内容
                String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);

                // 检查HTTP状态码，非200表示请求失败
                if (response.getStatusLine().getStatusCode() != 200) {
                    log.error("Python服务返回错误: {}", responseBody);
                    throw new RuntimeException("Python预测服务调用失败");
                }
                
                // 将JSON响应反序列化为响应对象
                ForecastResponse forecastResponse = objectMapper.readValue(responseBody, ForecastResponse.class);

                if (forecastResponse != null && forecastResponse.getPredictions() != null) {
                    log.info("预测完成: 预测点数={}", forecastResponse.getPredictions().size());
                }
                return forecastResponse;
            }

        }catch (Exception e){
            log.error("调用Python预测服务失败", e);
            throw new RuntimeException("调用Python预测服务失败", e);
        }
    }

    /**
     * 批量预测多个接口的流量
     * 
     * 应用场景：
     * - 系统初始化时，需要对所有活跃接口进行预测
     * - 定时任务批量刷新预测数据
     * - 减少HTTP请求次数，提高性能
     * 
     * 工作流程：
     * 1. 将多个接口的数据打包成一个请求
     * 2. Python服务并行处理多个接口的预测
     * 3. 返回批量预测的汇总结果
     * 
     * @param request 批量预测请求对象，包含：
     *                - interfaces: 多个接口的数据列表
     *                - forecastHours: 预测时长（小时）
     *                - modelType: 预测模型类型（建议使用ensemble集成模型）
     * @return 批量预测响应对象，包含：
     *         - total: 总接口数
     *         - success: 成功预测的接口数
     *         - failed: 失败的接口数
     *         - results: 每个接口的预测结果列表
     * @throws RuntimeException 当批量预测失败时抛出
     */
    public BatchForecastResponse batchForecast(BatchForecastRequest request) {
        // 构造批量预测接口URL
        String url = pythonServiceUrl + "/api/batch-forecast";

        try {
            // 创建HTTP POST请求
            HttpPost httpPost = new HttpPost(url);
            httpPost.setHeader("Content-Type", "application/json");
            httpPost.setConfig(requestConfig);

            // 序列化批量请求对象
            String jsonBody = objectMapper.writeValueAsString(request);
            httpPost.setEntity(new StringEntity(jsonBody, StandardCharsets.UTF_8));

            log.info("批量预测: 接口数={}", request.getInterfaces().size());
            
            // 执行HTTP请求
            try (CloseableHttpResponse response = httpClient.execute(httpPost);) {
                // 解析响应体
                String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);

                BatchForecastResponse batchResponse = objectMapper.readValue(
                        responseBody,
                        BatchForecastResponse.class
                );

                log.info("批量预测完成: 成功={}, 失败={}",
                        batchResponse.getSuccess(), batchResponse.getFailed());

                return batchResponse;
            }

        } catch (Exception e) {
            log.error("批量预测失败", e);
            throw new RuntimeException("批量预测失败", e);
        }
    }

    @PreDestroy
    public void destroy() throws IOException {
        if(httpClient!=null)httpClient.close();
    }

    // ==================== 请求和响应的数据传输对象（DTO） ====================
    
    /**
     * 单接口预测请求对象
     * 
     * 数据说明：
     * - interfaceId: 接口的唯一标识符
     * - historicalData: 历史流量数据，格式为 [{timestamp: "2025-11-01 10:00:00", request_count: 100}, ...]
     * - forecastHours: 预测未来多少小时的流量，推荐24小时（1天）或168小时（7天）
     * - modelType: 使用的预测模型
     *   * prophet: Facebook Prophet，适合有明显季节性的数据
     *   * lstm: 长短期记忆网络，适合复杂的非线性模式
     *   * arima: 自回归移动平均模型，适合平稳时间序列
     *   * ensemble: 集成模型，综合多个模型的预测结果
     * - confidenceLevel: 置信水平，0.95表示95%的置信区间
     */
    @Data
    public static class ForecastRequest {
        @JsonProperty("interface_id")  // JSON 中字段名为 interface_id
        private Long interfaceId;

        @JsonProperty("historical_data")  // 可不加，默认就是字段名
        private List<Map<String, Object>> historicalData;

        @JsonProperty("forecast_hours")  // JSON 中用 forecast_hours 而不是 forecastHours
        private Integer forecastHours = 24;

        @JsonProperty("model_type")  // JSON 中用 model_type 而不是 modelType
        private String modelType = "prophet";  // prophet/lstm/arima/ensemble

        @JsonProperty("confidence_level")  // JSON 中用 confidence_level
        private Double confidenceLevel = 0.95;
    }

    /**
     * 单接口预测响应对象
     * 
     * 数据说明：
     * - interfaceId: 接口ID
     * - modelType: 实际使用的模型类型
     * - forecastHours: 预测的时长
     * - predictions: 预测数据点列表
     * - accuracyMetrics: 模型准确度指标（如MAE、RMSE、MAPE等）
     * - generatedAt: 预测生成时间
     */
    @Data
    public static class ForecastResponse {
        @JsonProperty("interface_id")
        private Long interfaceId;

        @JsonProperty("model_type")
        private String modelType;

        @JsonProperty("forecast_hours")
        private Integer forecastHours;

        @JsonProperty("predictions")
        private List<PredictionPoint> predictions;

        @JsonProperty("accuracy_metrics")
        private Map<String, Object> accuracyMetrics;

        @JsonProperty("generated_at")
        private String generatedAt;
    }

    /**
     * 预测数据点
     * 
     * 数据说明：
     * - timestamp: 预测时间点（格式：yyyy-MM-dd HH:mm:ss）
     * - predictedValue: 预测的请求数量
     * - confidenceLower: 置信区间下界（最小可能值）
     * - confidenceUpper: 置信区间上界（最大可能值）
     * 
     * 置信区间的意义：
     * 95%的置信区间表示，实际值有95%的概率落在[confidenceLower, confidenceUpper]范围内
     */
    @Data
    public static class PredictionPoint {
        @JsonProperty("timestamp")
        private String timestamp;
        @JsonProperty("predicted_value")
        private Integer predictedValue;
        @JsonProperty("confidence_lower")
        private Integer confidenceLower;
        @JsonProperty("confidence_upper")
        private Integer confidenceUpper;
    }

    /**
     * 批量预测请求对象
     * 
     * 数据说明：
     * - interfaces: 多个接口的数据列表
     * - forecastHours: 统一的预测时长
     * - modelType: 统一使用的模型类型（批量预测建议使用ensemble）
     */
    @Data
    public static class BatchForecastRequest {
        private List<InterfaceData> interfaces;
        private Integer forecastHours = 24;
        private String modelType = "ensemble";
    }

    /**
     * 单个接口的数据
     * 用于批量预测请求
     */
    @Data
    public static class InterfaceData {
        private Long interfaceId;
        private List<Map<String, Object>> historicalData;
    }

    /**
     * 批量预测响应对象
     * 
     * 数据说明：
     * - total: 请求预测的接口总数
     * - success: 成功预测的接口数量
     * - failed: 失败的接口数量
     * - results: 每个接口的预测结果，格式为 [{interface_id: 1, predictions: [...], ...}, ...]
     */
    @Data
    public static class BatchForecastResponse {
        private Integer total;
        private Integer success;
        private Integer failed;
        private List<Map<String, Object>> results;
    }
}
